#pragma once 


#include <iostream>
#include <queue>
#include <pthread.h>
#include "Task.hpp"

#define NUM 6

class ThreadPool
{
  private:
    std::queue<Task> task_queue;
    int num;
    bool stop;
    pthread_mutex_t lock;
    pthread_cond_t cond;

    ThreadPool(int _num = NUM)
      :num(_num),
      stop(false)
  {
    pthread_mutex_init(&lock , nullptr);
    pthread_cond_init(&cond , nullptr);
  }

    ThreadPool(const ThreadPool& )
    {}

    static ThreadPool* single_instance;
  public:
   static ThreadPool* getinstance()
   {
     static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
     if (single_instance == nullptr)
     {
       pthread_mutex_lock(&mymutex);
       if (single_instance == nullptr)
       {
         single_instance = new ThreadPool();
         single_instance->InitThreadPool();
       }
     }

     return single_instance;
   }



    bool IsStop()
    {
      return this->stop;
    } 

    void Lock()
    {
      pthread_mutex_lock(&lock);
    }

    void Unlock()
    {
      pthread_mutex_unlock(&lock);
    }



    bool IsEmpty()
    {
      return task_queue.size() == 0;
    }

    static  void* ThreadRoutine(void* args)
    {
      ThreadPool* tp = (ThreadPool*)args;
      
      while(true)
      {
        Task t;
        tp->Lock();
        while (tp -> IsEmpty())
        {
          tp->ThreadWait();
        }
        tp->PopTask(t);
        tp->Unlock();
        t.ProcessOn();
      }
    }

    void ThreadWait()
    {
      pthread_cond_wait(&cond , &lock);
    }

    void ThreadWakeup()
    {
      pthread_cond_signal(&cond);
    }

    bool InitThreadPool()
    {
      for (int i = 0; i < num ; i++ )
      {
        pthread_t tid;
        if (0 == pthread_create(&tid , nullptr , ThreadRoutine, this) )
        {
          return false;
        }
      }

      return true;
    }

    void PushTak(const Task& task)
    {
      Lock();
      task_queue.push(task);
      Unlock();
      ThreadWakeup();
    }

    void PopTask(Task& task)
    {
      task = task_queue.front();
      task_queue.pop();
    }

    ~ThreadPool()
    {
      pthread_mutex_destroy(&lock);
      pthread_cond_destroy(&cond);
    }
};

ThreadPool* ThreadPool::single_instance = nullptr;
